home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK1.toast / Development Kits (Disc 1) / PCI Driver Development Kit / • Samples / Open Firmware Samples / SCSI example / AAPL-8250.of next >
Encoding:
Text File  |  1996-08-20  |  28.0 KB  |  932 lines  |  [TEXT/MPS ]

  1. \ FCode driver for NCR-8250 card
  2. \ -----------------------------------------------------------------------
  3. \    Note: This driver works when errors are not encounted.  I.e., the
  4. \    error handling is not correct and/or completely present.  The code
  5. \    should be used as indicative of how a "scsi" device is partitioned
  6. \    into the "scsi" bus node (which performs the interactions with the
  7. \    hardware) and child nodes (e.g., "sd" for scsi-disk) that are opened
  8. \    with explict addressing that indicates the target-id and LUN.
  9. \
  10. \    This driver is based upon the sample SCSI driver given in the IEEE-1275
  11. \    Open Firmware document.  In order to simplify testing, all of its 
  12. \    files have been included into this one file.
  13. \
  14. \    This driver also shows how to deal with endian-ness issues.  The
  15. \    scripts that get used to perform I/O have to be placed into memory
  16. \    so that they are properly fetched by the 825.  The code does explict
  17. \    "little-endian" stores that work regardless of the endian-ness of
  18. \    the memory system.
  19. \
  20. \    Also note that DMA-MAP-IN is used to get the appropriate physical
  21. \    address of the scripts and data buffers, since the 825 will fetch
  22. \    script code and transfer data using physical memory addresses.
  23. \ -----------------------------------------------------------------------
  24.  
  25. tokenizer[ hex 1000 0003 010000 ]tokenizer PCI-HEADER
  26.  
  27. FCode-Version2
  28. hex
  29.  
  30. " AAPL,NCR8250S" device-name        \ Name of device node
  31. " NCR,8250S" model                    \ Manufacturer's model number
  32. " scsi"            device-type            \ Device implements SCSI-2 method set
  33. " NCR,8250S" encode-string
  34.     " compatible"    property            \ same as this one
  35.  
  36. 0 0 my-space encode-phys
  37.     0 encode-int encode+ 0 encode-int encode+
  38. 0 0 02000014 my-space or encode-phys encode+
  39.     0 encode-int encode+ 100 encode-int encode+
  40.     " reg" property
  41.  
  42. external
  43.  
  44. \ These routines may be called by the children of this device.
  45. \ This card has no local buffer memory for the SCSI device, so it
  46. \ depends on its parent to supply DMA memory.  For a device with
  47. \ local buffer memory, these routines would probably allocate from
  48. \ that local memory.
  49.  
  50. : dma-alloc        ( n -- vaddr )     " dma-alloc" $call-parent     ;
  51. : dma-free        ( vaddr n -- )     " dma-free" $call-parent    ;
  52. : dma-sync        ( vaddr devaddr n -- )    " dma-sync" $call-parent  ;
  53. : dma-map-in    ( vaddr n cache? -- devaddr )     " dma-map-in" $call-parent  ;
  54. : dma-map-out    ( vaddr devaddr n -- )    " dma-map-out" $call-parent  ;
  55.  
  56.  
  57. \ -----------------------------------------------------------------------
  58. \ fload SCSIha.of
  59. \    This file contains the Hardware "Abstraction" layer of the driver.
  60. \    The device-specific drivers (e.g., "sd") call back into this code to
  61. \    handle all the details that are implementation-specific.
  62. \ -----------------------------------------------------------------------
  63.  
  64. headerless
  65.  
  66. \    Note:  since we actually only use a few of the registers for the 
  67. \    driver, defining them all is somewhat wasteful.  But they are all
  68. \    here for completeness.
  69.  
  70. struct    \ definition of registers; see NCR-825 document for their meanings
  71.     /C    field    >SCNTL0
  72.     /C    field    >SCNTL1
  73.     /C    field    >SCNTL2
  74.     /C    field    >SCNTL3
  75.     /C    field    >SCID
  76.     /C    field    >SXFER
  77.     /C    field    >SDID
  78.     /C    field    >GPREG
  79.     /C    field    >SFBR
  80.     /C    field    >SOCL
  81.     /C    field    >SSID
  82.     /C    field    >SBCL
  83.     /C    field    >DSTAT
  84.     /C    field    >SSTAT0
  85.     /C    field    >SSTAT1
  86.     /C    field    >SSTAT2
  87.     /L    field    >DSA
  88.     /C    field    >ISTAT
  89.     3 +
  90.     /C    field    >CTEST0
  91.     /C    field    >CTEST1
  92.     /C    field    >CTEST2
  93.     /C    field    >CTEST3
  94.     /L    field    >TEMP
  95.     /C    field    >DFIFO
  96.     /C    field    >CTEST4
  97.     /C    field    >CTEST5
  98.     /C    field    >CTEST6
  99.     3    field    >DBC
  100.     /C    field    >DCMD
  101.     /L    field    >DNAD
  102.     /L    field    >DSP
  103.     /L    field    >DSPS
  104.     /L    field    >SCRATCHA
  105.     /C    field    >DMODE
  106.     /C    field    >DIEN
  107.     /C    field    >DWT
  108.     /C    field    >DCNTL
  109.     /L    field    >ADDER
  110.     /C    field    >SIEN0
  111.     /C    field    >SIEN1
  112.     /C    field    >SIST0
  113.     /C    field    >SIST1
  114.     /C    field    >SLPAR
  115.     /C    field    >SWIDE
  116.     /C    field    >MACNTL
  117.     /C    field    >GPCNTL
  118.     /C    field    >STIME0
  119.     /C    field    >STIME1
  120.     /C    field    >RESPID0
  121.     /C    field    >RESPID1
  122.     /C    field    >STEST0
  123.     /C    field    >STEST1
  124.     /C    field    >STEST2
  125.     /C    field    >STEST3
  126.     /W    field    >SIDL
  127.     2 +
  128.     /W    field    >SODL
  129.     2 +
  130.     /W    field    >SBDL
  131.     2 +
  132.     /L    field    >SCRATCHB
  133. drop
  134.  
  135. 1 constant bus-reset \ Hardware result code that denotes an incoming
  136.                             \ SCSI bus reset
  137.  
  138. \ Now that we have a symbolic name for the size of the register block,
  139. \ we can declare the "reg" property
  140.  
  141. 0 value regs                     \ Virtual base address of device registers    
  142. 0 instance value my-id        \ host adapter's selection ID
  143. 0 instance value his-id        \ target's selection ID
  144. 0 instance value his-lun    \ target's unit number
  145.  
  146. \ Map device registers
  147.  
  148. : map     ( -- )
  149.     " assigned-addresses" get-my-property abort" no ASSIGNED ADDRESSES"
  150.     begin
  151.         dup 0> while
  152.         decode-phys dup FF and 14 = if
  153.             100 " map-in" $call-parent     to regs
  154.             2drop exit
  155.         then
  156.         3drop decode-int drop decode-int drop
  157.         repeat
  158.     true abort" no MEMORY ADDRESS"
  159.     ;
  160. : unmap    ( -- )
  161.     regs 100    " map-out" $call-parent    0 to regs
  162.     ;
  163.  
  164. : SIST@ regs >SIST1 rb@ 8 << regs >SIST0 rb@ or ;
  165. : ISTAT@ regs >ISTAT rb@ ;
  166. : ISTAT! regs >ISTAT rb! ;
  167. : DSTAT@ regs >DSTAT rb@ ;
  168. : SBCL@ regs >SBCL rb@ ;
  169. : DSP@ regs >DSP rl@ ;
  170. : DSP! regs >DSP rl! ;
  171. : DCMD@ regs >DCMD rb@ ;
  172.  
  173. create reset-done-time 0 ,
  174. create resetting false ,
  175.  
  176. \ 5 seconds appears to be about the right length of time to wait after
  177. \ a reset, considering a variety of disparate devices.
  178. d# 5000 value scsi-reset-delay
  179.  
  180. : reset-wait  ( -- )
  181.     resetting @     if
  182.         begin     get-msecs reset-done-time @ -  0>=     until
  183.         resetting off
  184.     then
  185.     begin
  186.         istat@ 3 and while
  187.         sist@ drop        \ clear interrupt bits
  188.         dstat@ drop
  189.         repeat
  190. ;
  191.  
  192. : RESET-825            ( -- )        \ reset the chip back to idle state
  193.     begin                        \ clear up out internal state
  194.         istat@ 3 and while
  195.         sist@ drop                \ clear interrupt bits
  196.         dstat@ drop
  197.         repeat
  198.     04 regs >CTEST3 rb!            \ Clear DMA FIFO
  199.     ;
  200.  
  201. : RESET-SCSI-BUS    ( -- )
  202.     8 regs >SCNTL1 rb!            \ assert RST/
  203.     5 ms                        \ wait for 5 msecs
  204.     0 regs >SCNTL1 rb!            \  then, de-assert
  205.     5 ms
  206.     reset-825
  207.  
  208.     \ After resetting the SCSI bus, we have to give the target devices
  209.     \ some time to initialize their microcode.  Otherwise the first command
  210.     \ may hang, as with some older controllers.    We note the time when it
  211.     \ is okay to access the bus (now plus some delay), and "execute-command"
  212.     \ will delay until that time is reached, if necessary.
  213.     \ This allows us to overlap the delay with other work in many cases.
  214.  
  215.     get-msecs scsi-reset-delay + reset-done-time !    resetting on
  216. ;
  217.  
  218. 0 value scsi-time        \ Maximum command time in milliseconds
  219. 0 value time-limit        \ Ending time for command
  220. 0 value istat            \ value of ISTAT reg upon completion
  221. 0 value dstat            \ value of DSTAT reg upon completion
  222. 0 value sist            \ value of SIST<1:0>
  223.  
  224. 0 value devaddr
  225.  
  226. 0 value script-bfr        \ address of scripting area
  227. 0 value script-bfr-p
  228. 0 value script-ptr        \ ptr to next script loc'n
  229. 0 value status            \ status byte addr (virtual)
  230. 0 value status-p        \ status byte addr (physical)
  231. 0 value message            \ status byte addr (virtual)
  232. 0 value message-p        \ status byte addr (physical)
  233. 0 value cmd-bfr            \ address of copied command area (always DMA-MAP'd-IN)
  234. 0 value cmd-bfr-p        \ physical address of same
  235.  
  236. : c!++    ( b addr -- addr' )        tuck c! 1+     ;
  237. : c@++    ( addr -- b addr' )        dup c@ swap 1+ ;
  238. : 4c!-le        ( u a -- )    \ store script word in little-endian
  239.     >r lbflip lbsplit r> c!++ c!++ c!++ c! ;
  240. : 4c@-le        ( a -- u )    \ fetch script word in little-endian
  241.     c@++ c@++ c@++ c@ bljoin ;
  242. : script,    ( dbcmd-dbc dsps -- )
  243.     swap script-ptr 4c!-le
  244.     script-ptr 4 + 4c!-le
  245.     script-ptr 8 + to script-ptr
  246.     ;
  247.  
  248. \ Returns true if select failed
  249. : (exec)     ( dma-adr,len dir cmd-adr,len -- hwresult )
  250.     reset-wait                \ Delay until any prior reset operation to done
  251.  
  252.     \ create a script for this command
  253.     script-bfr to script-ptr                    \ startup ptr
  254.     40000000 his-id d# 16 << or 0 script,        \ SELECT
  255.     tuck cmd-bfr swap move                        \ copy command to "well known place"
  256.     his-lun 5 << cmd-bfr 1+ tuck c@ or swap c!    \ add in LUN
  257.     02000000 or cmd-bfr-p script,                \ COMMAND-OUT
  258.     over ( len ) if                                \ DATA phase expected?
  259.         ( dir ) if                                \ READ
  260.             01000000                                \ ->DATA-IN
  261.           else                                    \ WRITE
  262.             00000000                            \ ->DATA-OUT
  263.           then
  264.         or swap script,                            \ DATA-??
  265.       else                                        \ no DATA phase, drop args
  266.         drop 2drop
  267.     then
  268.     03000001 status-p script,                    \ STATUS-IN
  269.     07000001 message-p script,                    \ MESSAGE-IN
  270.     60000440 0 script,                            \ clear /SACK & CARRY
  271.     98200000 0 script,                            \ INTERRUPT (CARRY clear)
  272.  
  273.     script-bfr-p dsp!                            \ start it up
  274.  
  275.     get-msecs scsi-time +  to time-limit        \ Set the time limit
  276.  
  277.     begin                                        \ major loop
  278.  
  279.         begin                                        \ sub-loop for command continuation after short transfer
  280.             istat@ 3 and dup ?dup if                \ save status if non-zero
  281.                 to istat
  282.               then
  283.             0= while                                \ Wait until something happens
  284.             scsi-time  if                            \ If timeout is enabled, and
  285.                 get-msecs time-limit -    0>=  if        \ the time-limit has been reached,
  286.                     reset-scsi-bus     true     exit    \ reset the bus and return error
  287.                   then
  288.               then
  289.             repeat
  290.  
  291.         istat 1 and if                                \ DMA interrupt
  292.             dstat@ to dstat                            \ save for debugging
  293.           then
  294.  
  295.         istat 2 and if                                \ SCSI interrupt
  296.             sist@ to sist                            \ combined SCSI status
  297.             sist 0400 and if                        \ STO -> device not present
  298.                 reset-825     true     exit
  299.               then    
  300.             sist 0004 and if                        \ if UDC and not my INTR, error
  301.                 dcmd@ 98 <> exit
  302.               then
  303.             sist 0002 and if                        \ RST, simply restart
  304.                 script-bfr-p dsp!
  305.               then
  306.             sist 0080 and if                        \ M/A, phase mis-match; possibly short data transfer
  307.                 dsp@                                \ (physical) address of aborted command
  308.                 script-bfr-p - script-bfr +            \ converting to virtual address
  309.                 4c@-le 03000001 = if                \ if its the STATUS-IN, we had short data
  310.                     dsp@ dsp!                        \  so, try to restart it
  311.                   then
  312.               then
  313.           then
  314.  
  315.         again                                        \ major loop end
  316. ;
  317.  
  318. \ Returns true if select failed
  319. : EXECUTE-COMMAND ( data-adr,len dir cmd-adr,len -- hwresult | statbyte false )
  320.     \ Put dir and cmd-adr,len on the return stack temporarily, to get them
  321.     \ out of the way so we can work on the DMA data buffer.
  322.  
  323.     >r >r >r                              ( data-adr,len )
  324.  
  325.     dup  if                              ( data-adr,len )
  326.  
  327.         \ If the data transfer has a non-zero length, we have to map it in
  328.  
  329.         2dup    false     dma-map-in      ( data-adr,len dma )
  330.         2dup swap  r> r> r>          ( data-adr,len dma dma,len dir cmd-adr,len)
  331.  
  332.         (exec)                          ( data-adr,len phys hwres)
  333.  
  334.         >r swap dma-map-out    r>      ( hwresult )
  335.     else                                  ( data-adr,len )
  336.         r> r> r>     (exec)              ( hwresult )
  337.     then                                  ( hwresult )
  338.  
  339.     ?dup    0=     if                                                ( hwresult | )
  340.         status c@    false             \ Command finished; return status byte and false
  341.     then                                                            ( hwresult | statbyte 0 )
  342. ;
  343.  
  344. external
  345.  
  346. : RESET    ( -- )  map     reset-scsi-bus  unmap    ;
  347. \ reset      \ Reset the SCSI bus when we are probed.
  348.  
  349. : set-address    ( unit target -- )
  350.     to his-id  to his-lun
  351. ;
  352. : set-timeout    ( msec-time -- )
  353.     to scsi-time
  354.     ;
  355.  
  356. headerless
  357.  
  358. : config-w!    " config-w!" $call-parent ;
  359. : config-w@ " config-w@" $call-parent ;
  360.  
  361. : open-hardware  ( -- flag )
  362.     map                                \ map-in our regs
  363.     6 my-space 4 + tuck config-w@ or swap config-w!        \ enable Memory Space & Master Mode
  364.     h# 33    regs >SCNTL3 rb!        \ initialize clocks
  365.     h# 0C    regs >STIME0 rb!        \ selection time-out
  366.     7        regs >SCID rb!            \ our ID
  367.  
  368.     true
  369. ;
  370. : reopen-hardware     ( -- flag )  true  ;
  371.  
  372. : close-hardware    ( -- )  unmap    ;
  373. : reclose-hardware  ( -- )     ;
  374.  
  375. \ -----------------------------------------------------------------------
  376. \ fload SCSIhacom.of
  377. \
  378. \ The following code is intended to be independent of the details of the
  379. \ SCSI hardware implementation.    It is loaded after the hardware-dependent
  380. \ file that defines execute-command, set-address, open-hardware, etc.
  381. \ -----------------------------------------------------------------------
  382.  
  383.  
  384. 0 value inq-buf            \ inquiry data buffer
  385. 0 value sense-buf        \ holds extended error information
  386.  
  387. 0 value #retries    ( -- n )             \ number of times to retry SCSI transaction
  388.  
  389. \ Classifies the sense condition as either okay (0), retryable (1),
  390. \ or non-retryable (-1)
  391. : classify-sense    ( -- 0 | 1 | -1 )
  392. \    debug?  if    ." Sense:  " sense-buf 11 cdump ."    ..." cr    then
  393.     sense-buf
  394.  
  395.     \ Make sure we understand the error class code
  396.     dup c@  7F and 70 <>     if  drop -1 exit     then        ( sense-buf )
  397.  
  398.     \ Check for filemark, end-of-media, or illegal block length
  399.     2+ c@ dup  E0    and  if    drop -1 exit  then        ( sense-key-byte )
  400.  
  401.     0F and                                                         ( sense-key )
  402.  
  403.     \ no_sense(0) and recoverable(1) are okay
  404.     dup 1 <=     if  drop 0 exit    then                        ( sense-key )
  405.  
  406.     \ not-ready(2) and attention(6) are retryable
  407.     dup 2 =    swap 6 =     or  if    1    else    -1     then
  408. ;
  409.  
  410. 0 value open-count
  411.  
  412. external
  413.  
  414. \ The SCSI device node defines an address space for its children.     That
  415. \ address space is of the form "target#,unit#".     target# and unit# are
  416. \ both integers.    parse-2int converts a text string (e.g. "3,4") into
  417. \ a pair of binary integers.
  418.  
  419. : DECODE-UNIT    ( addr len -- unit# target# )     parse-2int  ;
  420.  
  421. : OPEN  ( -- flag )
  422.     open-count    if
  423.         reopen-hardware  dup     if  open-count 1+ to open-count     then
  424.         exit
  425.      else
  426.         open-hardware    dup  if
  427.             100 dma-alloc to sense-buf
  428.             100 dma-alloc to inq-buf
  429.             100 dma-alloc to script-bfr            \ get area for our scripts (virtual address)
  430.             script-bfr 100 false dma-map-in
  431.                 to script-bfr-p                        \ physical address
  432.             script-bfr 0D0 + to status
  433.             script-bfr-p 0D0 + to status-p
  434.             script-bfr 0D4 + to message
  435.             script-bfr-p D4 + to message-p
  436.             script-bfr 0E0 + to cmd-bfr            \ place to copy command
  437.             script-bfr-p 0E0 + to cmd-bfr-p
  438.             1 to open-count
  439.          then
  440.      then
  441.     ;
  442. : CLOSE    ( -- )
  443.     open-count 1- to open-count
  444.     open-count    if
  445.         reclose-hardware
  446.      else
  447.         close-hardware
  448.         script-bfr    100 dma-free
  449.         inq-buf        100 dma-free
  450.         sense-buf    100 dma-free
  451.      then
  452.     ;
  453.  
  454. : MAX-TRANSFER    ( -- n )
  455. \ Note: due to a bug in the A2 ROM, this value is smaller than it should be.
  456. \ It should be 01000000 (i.e., 16 MB) due to the 24-bit DBC register. 
  457.     1000    ( 4 KB )
  458.     ;
  459.  
  460. headerless
  461.  
  462. : get-sense     ( -- )        \ Issue REQUEST SENSE, which is not supposed to fail
  463.     sense-buf FF  true  " "(03000000FF00)"     execute-command    0=     if  drop  then
  464. ;
  465.  
  466. \ Give the device a little time to recover before retrying the command.
  467. : delay-retry    ( -- )    1000 0 do loop     ;
  468.  
  469. 0 value statbyte    \ Local variable used by retry?
  470.  
  471. \ RETRY? is used by RETRY-COMMAND to determine whether or not to retry the
  472. \ command, considering the following factors:
  473. \    - Success or failure of the command at the hardware level (failure at
  474. \      this level is usually fatal, except in the case of an incoming bus reset)
  475. \    - The value of the status byte returned by the command
  476. \    - The condition indicated by the sense bytes
  477. \    - The number of previous retries
  478. \
  479. \ The input arguments are as returned by "scsi-exec"
  480. \ On output, the top of the stack is true if the command is to be retried,
  481. \ otherwise the top of the stack is false and the results that should be
  482. \ returned by retry-command are underneath it; those results indicate the type
  483. \ of error that occurred.
  484.  
  485. : retry?     ( hw-result | statbyte 0 -- true | [[sensebuf] f-hw] error? false )
  486.     case
  487.         0              of    to statbyte     endof  \ No hardware error; continue checking
  488.         bus-reset  of    true exit     endof      \ Retry after incoming bus reset
  489.         ( hw-result )    true false exit              \ Other hardware errors are fatal
  490.     endcase
  491.  
  492.     statbyte 0=     if  false false exit  then      \ If successful, return    "no-error"
  493.  
  494.     statbyte     2 and  if      \ "Check Condition", so get extended status
  495.         get-sense  classify-sense    case                         ( -1|0|1 )
  496.              \ If the sense information says "no sense", return "no-error"
  497.              0     of  false false exit                             endof
  498.  
  499.             \ If the error is fatal, return "sense-buf,valid,statbyte"
  500.             -1     of  sense-buf false statbyte false     exit     endof
  501.         endcase
  502.  
  503.         \ Otherwise, the error was retryable.    However, if we have
  504.         \ have already retried the specified number of times, don't
  505.         \ retry again; instead return sense buffer and status.
  506.         #retries 0=     if  sense-buf false statbyte false     exit     then
  507.     then
  508.  
  509.     \ Don't retry if vendor-unique, reserved, intermediate, or
  510.     \ "condition met/good" bits are set. Return "no-sense,status"
  511.     statbyte F5 and  if    true statbyte false    exit    then
  512.  
  513.     \ Don't retry if we have already retried the specified number
  514.     \ of times.     Return "no-sense,status"     
  515.     #retries 0=     if  true statbyte false  exit  then
  516.  
  517.     \ Otherwise, it was either a busy or a retryable check condition,
  518.     \ so we retry.
  519.  
  520.     true
  521. ;
  522.  
  523. external
  524.  
  525. \ RETRY-COMMAND executes a SCSI command.    If a check condition is indicated,
  526. \ performs a "get-sense" command.  If the sense bytes indicate a non-fatal
  527. \ condition (e.g. power-on reset occurred, not ready yet, or recoverable
  528. \ error), the command is retried until the condition either goes away or
  529. \ changes to a fatal error.
  530. \
  531. \ The command is retried until:
  532. \ a) The command succeeds, or
  533. \ b) The select fails, or dma fails, or
  534. \ c) The sense bytes indicate an error that we can't retry at this level
  535. \ d) The number of retries is exceeded.
  536.  
  537. \ #retries is number of times to retry (0: don't retry, -1: retry forever)
  538. \
  539. \ sensebuf is the address of the sense buffer; it is present only
  540. \ if f-hw is 0 and error? is non-zero.     The length of the sense buffer
  541. \ is 8 bytes plus the value in byte 7 of the sense buffer.
  542. \
  543. \ f-hw is non-zero if there is a hardware error -- dma fails, select fails,
  544. \ etc -- or if the status byte was neither 0 (okay) nor 2 (check condition)
  545. \
  546. \ error? is non-zero if there is a transaction error.     If error? is 0,
  547. \ f-hw and sensebuf are not returned.
  548. \
  549. \ If sensebuf is returned, the contents are valid until the next call to
  550. \ retry-command.    sensebuf becomes inaccessable when this package is closed.
  551. \
  552. \ dma-dir is necessary because it is not always possible to infer the DMA
  553. \ direction from the command.
  554.  
  555. \ Local variables used by retry-command?
  556.  
  557. 0 value dbuf                 \ Data transfer buffer
  558. 0 value dlen                 \ Expected length of data transfer
  559. 0 value direction-in         \ Direction for data transfer
  560.  
  561. -1 value cbuf                 \ Command base address
  562.  0 value clen                 \ Actual length of this command
  563.  
  564. : retry-command  ( dma-buf dma-len dma-dir cmdbuf cmdlen #retries -- ... )
  565.               ( ... -- [[sensebuf] f-hw] error? )
  566.     to #retries      to clen  to cbuf  to direction-in     to dlen     to dbuf
  567.  
  568.     begin
  569.         dbuf dlen  direction-in     cbuf clen    execute-command  ( hwerr | stat 0 )
  570.         retry? while
  571.         #retries 1- to #retries
  572.         delay-retry
  573.         repeat
  574. ;
  575.  
  576. \ Collapses the complete error information returned by retry-command into
  577. \ a single error/no-error flag.
  578.  
  579. : error?     ( false | true true | sensebuf false true -- error? )
  580.     dup  if    swap 0=    if     nip    then    then
  581. ;
  582.  
  583. \ Simplified "retry-command" routine for commands with no data transfer phase
  584. \ and simple error checking requirements.
  585.  
  586. : NO-DATA-COMMAND     ( cmd len -- error? )
  587.     >r >r     0 0 true  r> r>  -1    retry-command error?
  588.     ;
  589.  
  590. \ short-data-command executes a command with the following characteristics:
  591. \    a) The data direction is incoming
  592. \    b) The data length is less than 256 bytes
  593.  
  594. \ The host adapter driver is responsible for supplying the DMA data
  595. \ buffer; if the command succeeds, the buffer address is returned.
  596. \ The buffer contents become invalid when another SCSI command is
  597. \ executed, or when the driver is closed.
  598.  
  599. : short-data-command     ( data-len cmdbuf cmdlen -- true | buffer false )
  600.     >r >r     inq-buf swap    true    r> r> -1     retry-command      ( retry-cmd-results )
  601.     error?  dup 0=     if  inq-buf swap     then
  602. ;
  603.  
  604. headerless
  605.  
  606. \ Here begins the implementation of "show-children", a word that
  607. \ is intended to be executed interactively, showing the user the
  608. \ devices that are attached to the SCSI bus.
  609.  
  610. \ Tool for storing a big-endian 24-bit number at an unaligned address
  611.  
  612. : 3c!     ( n addr -- )     >r lbsplit drop    r@ c!     r@ 1+ c!  r> 2+ c!    ;
  613.  
  614.  
  615. \ Command block template for Inquiry command
  616.  
  617. : inquiry  ( -- error? )
  618.     \ 8 retries should be more than enough; inquiry commands aren't
  619.     \ supposed to respond with "check condition".
  620.  
  621.     inq-buf FF    true    " "(12000000FF00)"    8    retry-command    error?
  622. ;
  623.  
  624. \ Returns true if the target number "select-id" responds to the inquiry
  625. \ command.
  626. : probe-target     ( select-id -- present? )
  627.     0 swap set-address    inquiry 0=
  628. ;
  629.  
  630.  
  631. \ Reads the indicated byte from the Inquiry data buffer
  632.  
  633. : inq@  ( offset -- value )  inq-buf +     c@  ;
  634.  
  635. : .scsi1-inquiry    ( -- )  inq-buf 5 ca+  4 inq@ 0FA min type    ;
  636. : .scsi2-inquiry    ( -- )  inq-buf 8 ca+  d# 28 type     ;
  637.  
  638. \ Displays the results of an Inquiry command to the indicated device
  639.  
  640. : show-lun    ( unit target -- )
  641.     over swap  set-address                        ( unit )
  642.     inquiry    if     drop exit    then                ( unit )
  643.     0 inq@  7F    =    if     drop exit    then        ( unit )
  644.     ."      Unit " . ."     "
  645.     1 inq@  80 and     if  ." Removable "    then
  646.     0 inq@  case
  647.         0 of    ." Disk "                  endof
  648.         1 of    ." Tape "                  endof
  649.         2 of    ." Printer "              endof
  650.         3 of    ." Processor "              endof
  651.         4 of    ." WORM "                  endof
  652.         5 of    ." Read Only device"      endof
  653.         ( default ) ." Device type " dup .h
  654.     endcase
  655.  
  656.     1 inq@  7F and     ?dup     if  ."    Qualifier  " .h  then
  657.  
  658.     4 spaces
  659.     3 inq@ 0F and    2 =  if    .scsi2-inquiry     else     .scsi1-inquiry  then
  660.     cr
  661. ;
  662.  
  663. external
  664.  
  665. \ Searches for devices on the SCSI bus, displaying the Inquiry information
  666. \ for each device that responds.
  667.  
  668. : show-children  ( -- )
  669.     open    0=     if  ." Can't open SCSI host adapter" cr    exit    then
  670.     cr
  671.     8 0  do
  672.         i probe-target     if
  673.             ." Target " i . cr
  674.             8 0  do    i j show-lun  loop
  675.         then
  676.     loop
  677.  
  678.     close
  679. ;
  680.  
  681. headerless
  682.  
  683. \ -----------------------------------------------------------------------
  684. \ fload SCSIdisk.of
  685. \ SCSI disk package implementing a "block" device-type interface.
  686. \ -----------------------------------------------------------------------
  687.  
  688. new-device    \ SD
  689.  
  690. " sd"         device-name
  691. " block"        device-type
  692.  
  693. \ -----------------------------------------------------------------------
  694. \ fload scsicom.of        \ Utility routines for SCSI commands
  695. \ This file contains some words which are useful for both SCSI disk and
  696. \ SCSI tape device drivers.
  697. \
  698. \ The SCSI disk and SCSI tape packages need to export dma-alloc and dma-free
  699. \ methods so the deblocker can allocate DMA-capable buffer memory.
  700. \ -----------------------------------------------------------------------
  701.  
  702. external
  703. : dma-alloc     ( n -- vaddr )  " dma-alloc" $call-parent  ;
  704. : dma-free     ( vaddr n -- )  " dma-free"    $call-parent  ;
  705. headerless
  706.  
  707. : parent-max-transfer  ( -- n )    " max-transfer"  $call-parent     ;
  708.  
  709.  
  710. \ Calls the parent device's "retry-command" method.  The parent device is
  711. \ assumed to be a driver for a SCSI host adapter (device-type = "scsi")
  712.  
  713. : retry-command  ( dma-addr dma-len dma-dir cmd-addr cmd-len #retries -- ... )
  714.               ( ... -- false )                     \ No error
  715.               ( ... -- true true )                 \ Hardware error
  716.               ( ... -- sensebuf false true )     \ Fatal error with extended status
  717.     " retry-command" $call-parent
  718. ;
  719.  
  720.  
  721. \ Simplified command execution routines for common simple command forms
  722.  
  723. : no-data-command     ( cmd len -- error? )  " no-data-command" $call-parent  ;
  724.  
  725. : short-data-command     ( data-len cmdbuf cmdlen -- true | buffer false )
  726.     " short-data-command" $call-parent
  727. ;
  728.  
  729.  
  730. \ -----------------------------------------------------------------------
  731. \ SCSIdisk.of (continued)
  732. \ Some tools for reading and writing 2, 3, and 4 byte numbers to and from
  733. \ SCSI command and data buffers.     The ones defined below are used both in
  734. \ the SCSI disk and the SCSI tape packages.    Other variations that are
  735. \ used only by one of the packages are defined in the package where they
  736. \ are used.
  737. \ -----------------------------------------------------------------------
  738.  
  739. : 3c!        ( n addr -- )     >r lbsplit drop    r> c!++ c!++ c!    ;
  740.  
  741. : c@--    ( addr -- n addr' )     dup c@    swap 1-    ;
  742. : 3c@        ( addr -- n )     2 +    c@-- c@--    c@            0    bljoin  ;
  743. : 4c@        ( addr -- n )     3 +    c@-- c@-- c@--     c@        bljoin  ;
  744.  
  745.  
  746. \ "Scratch" command buffer useful for construction of read and write commands
  747.  
  748. create cmdbuf    0 c, 0 c, 0 c, 0 c, 0 c, 0 c, 0 c, 0 c, 0 c, 0 c,
  749. : cb!     ( byte index -- )  cmdbuf + c!    ;            \ Write byte to command buffer
  750.  
  751.  
  752. \ The deblocker converts a block/record-oriented interface to a byte-oriented
  753. \ interface, using internal buffering.     Disk and tape devices are usually
  754. \ block or record oriented, but the OBP external interface is byte-oriented,
  755. \ in order to be independent of particular device block sizes.
  756.  
  757. 0 instance value deblocker
  758. : init-deblocker    ( -- okay? )
  759.     0 0  " deblocker"     $open-package     to deblocker
  760.     deblocker if
  761.         true
  762.     else
  763.         ." Can't open deblocker package"     cr  false
  764.     then
  765. ;
  766.  
  767. \ 0 means no timeout
  768. : set-timeout    ( msecs -- )  " set-timeout" $call-parent     ;
  769.  
  770. 0 instance value offset-low      \ Offset to start of partition
  771. 0 instance value offset-high
  772.  
  773. 0 instance value label-package
  774.  
  775. \ Sets offset-low and offset-high, reflecting the starting location of the
  776. \ partition specified by the "my-args" string.
  777.  
  778. : init-label-package     ( -- okay? )
  779.     0 to offset-high    0 to offset-low
  780.     my-args    " disk-label"    $open-package to label-package
  781.     label-package    if
  782.         0 0  " offset" label-package $call-method     to offset-high to offset-low
  783.         true
  784.     else
  785.         ." Can't open disk label package"  cr    false
  786.     then
  787. ;
  788.  
  789.  
  790. \ Ensures that the disk is spinning, but doesn't wait forever
  791.  
  792. : timed-spin  ( -- error? )
  793.     d# 15000 set-timeout
  794.     " "(1B0100000100)"    no-data-command
  795.     d# 1000 set-timeout
  796.     ;
  797.  
  798. 0 instance value /block              \ Device native block size
  799.  
  800. : read-block-size     ( -- n )      \ Ask device about its block size
  801.     \ First try "mode sense" - data returned in bytes 9,10,11
  802.  
  803.     d# 12     " "(1A0000000C00)"     short-data-command    if     0     else     9 + 3c@     then
  804.  
  805.     ?dup    if     exit     then
  806.  
  807.     \ Failing that, try "read-capacity" - data returned in bytes 4,5,6,7
  808.  
  809.     8    " "(250000000C0000000000)"     short-data-command     if  0  else  4 + 4c@  then
  810.  
  811.     ?dup    if     exit     then
  812.  
  813.     d# 512                    \ Default to 512 if the device won't tell us
  814.     ;
  815.  
  816. \ Read or write "#blks" blocks starting at "block#" into memory at "addr"
  817. \ Input? is true for reading or false for writing.
  818. \ command is  8  for reading or    h# a    for writing
  819.  
  820. : 2c!     ( n addr -- )     >r lbsplit 2drop     r> c!++    c!  ;
  821. : 4c!     ( n addr -- )     >r lbsplit             r> c!++ c!++ c!++ c!  ;
  822.  
  823. : r/w-blocks  ( addr block# #blks input? command -- actual# )
  824.     3 pick  100000 u>=  if        \ Use 10-byte form  ( addr block# #blks dir cmd )
  825.         h# 20 or 0 cb!    \ 28 (read) or 2A (write)    ( addr block# #blks dir )
  826.         -rot swap                                    ( addr dir #blks block# )
  827.         cmdbuf 2 + 4c!                                ( addr dir #blks )
  828.         dup cmdbuf 7 + 2c!
  829.       else                        \ Use 6-byte form    ( addr block# #blks dir cmd )
  830.         0 cb!                                        ( addr block# #blks dir )
  831.         -rot swap                                    ( addr dir #blks block# )
  832.         cmdbuf 1+ 3c!                                ( addr dir #blks )
  833.         dup 4 cb!                                    ( addr dir #blks )
  834.       then
  835.     dup >r                                            ( addr input? #blks )
  836.     /block * swap                                    ( addr #bytes input? )
  837.     cmdbuf dup c@ 20 and if d# 10 else 6 then -1    ( addr #bytes input? cmd cmdlen #retries )
  838.     retry-command    if                                ( [ sensebuf ] hw? )
  839.         0= if     drop     then     r> drop 0
  840.       else
  841.         r>
  842.       then      ( actual# )
  843. ;
  844.  
  845. external
  846.  
  847. \ These three methods are called by the deblocker.
  848.  
  849. \ Return device block size; cache it the first time we find the information
  850. \ This method is called by the deblocker
  851. : BLOCK-SIZE  ( -- n )
  852.     /block  if    /block exit     then             \ Don't ask if we already know
  853.  
  854.     read-block-size dup to /block
  855. ;
  856. : MAX-TRANSFER     ( -- n )    parent-max-transfer    ;
  857. : READ-BLOCKS     ( addr block# #blocks -- #read )
  858.     0 >r                                    \ big block done counter
  859.     begin 100 over < while            \ do it in smaller pieces
  860.         2 pick 2 pick 100 true  08  r/w-blocks
  861.         r> + >r
  862.         rot 100 /block * +            \ update addr
  863.         rot 100 +                    \ block#
  864.         rot 100 -                    \ #blocks
  865.         repeat
  866.     ( addr' block#' #blocks' ) ?dup if
  867.         true 08 r/w-blocks            \ do last segment
  868.         r> +
  869.       else                            \ just ended right
  870.         2drop r>
  871.       then
  872.         ;
  873. : WRITE-BLOCKS     ( addr block# #blocks -- #written )
  874.         0 >r                                \ big block done counter
  875.     begin 100 over < while            \ do it in smaller pieces
  876.         2 pick 2 pick 100 false  0A  r/w-blocks
  877.         r> + >r
  878.         rot 100 /block * +            \ update addr
  879.         rot 100 +                    \ block#
  880.         rot 100 -                    \ #blocks
  881.         repeat
  882.     ( addr' block#' #blocks' ) ?dup if
  883.         false 0A r/w-blocks            \ do last segment
  884.         r> +
  885.       else                            \ just ended right
  886.         2drop r>
  887.       then
  888.     ;
  889.  
  890. \ Methods used by external clients
  891.  
  892. : OPEN  ( -- flag )
  893.     my-unit " set-address" $call-parent
  894.  
  895.     \ It might be a good idea to do an inquiry here to determine the
  896.     \ device configuration, checking the result to see if the device
  897.     \ really is a disk.
  898.  
  899.     \ Make sure the disk is spinning
  900.  
  901.     timed-spin    if     false exit     then
  902.  
  903.     block-size to /block
  904.  
  905.     init-deblocker     0=  if    false exit    then
  906.  
  907.     init-label-package  0=    if
  908.         deblocker close-package false exit
  909.     then
  910.  
  911.     true
  912.     ;
  913.  
  914. : CLOSE    ( -- )
  915.     label-package ?dup  if  close-package  then
  916.     deblocker ?dup  if  close-package  then
  917.     ;
  918.  
  919. : SEEK  ( offset.low offset.high -- okay? )
  920.     offset-low offset-high    d+     " seek"      deblocker $call-method
  921.     ;
  922.  
  923. : READ  ( addr len -- actual-len )    " read"    deblocker $call-method  ;
  924. : WRITE ( addr len -- actual-len )    " write" deblocker $call-method  ;
  925. : LOAD  ( addr -- size )            " load"    label-package $call-method  ;
  926.  
  927. finish-device    \ SD
  928.  
  929. FCode-End
  930.  
  931. PCI-END
  932.